package com.snowfield.lbcommon.utils;

import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.ssl.TLS;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;

import javax.net.ssl.SSLContext;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class HttpsUtils {
    private static final String HTTP = "http";
    private static final String HTTPS = "https";
    private static PoolingHttpClientConnectionManager connectionManager = null;

    static {
        try {
            // Trust standard CA and those trusted by our custom strategy
            final SSLContext sslContext = SSLContexts.custom()
                    // Custom TrustStrategy implementations are intended for verification
                    // of certificates whose CA is not trusted by the system, and where specifying
                    // a custom truststore containing the certificate chain is not an option.
                    .loadTrustMaterial((chain, authType) -> {
                        // Please note that validation of the server certificate without validation
                        // of the entire certificate chain in this example is preferred to completely
                        // disabling trust verification, however this still potentially allows
                        // for man-in-the-middle attacks.

//                        final X509Certificate cert = chain[0];
//                        return "CN=httpbin.org".equalsIgnoreCase(cert.getSubjectDN().getName());

                        //信任所有
                        return true;
                    })
                    .build();
            final SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
                    .setSslContext(sslContext)
                    .setHostnameVerifier(new NoopHostnameVerifier())
                    .build();

            //设置TLS配置信息
            TlsConfig tlsConfig = TlsConfig.custom()
                    .setHandshakeTimeout(Timeout.ofSeconds(30))
                    .setSupportedProtocols(TLS.V_1_3, TLS.V_1_2, TLS.V_1_1) //支持的TLS协议版本
//                    .setSupportedCipherSuites("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
//                            "TLS_RSA_WITH_AES_256_CBC_SHA256",
//                            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
//                            "TLS_RSA_WITH_AES_128_CBC_SHA",
//                            "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
//                            "TLS_RSA_WITH_AES_256_GCM_SHA384") //支持的加密套件
                    .build();

            // 设置Socket配置
            SocketConfig socketConfig = SocketConfig.custom()
                    .setSoTimeout(Timeout.ofSeconds(60L))
                    .build();
            //创建连接管理器
            connectionManager =
                    PoolingHttpClientConnectionManagerBuilder.create()
                            .setSSLSocketFactory(sslSocketFactory)
                            .setDefaultTlsConfig(tlsConfig)
                            .setDefaultSocketConfig(socketConfig)
                            .setMaxConnTotal(1000) //最大连接数
                            .setMaxConnPerRoute(50) //每个路由最大连接数
                            .build();

            // 设置连接配置
            ConnectionConfig connectionConfig = ConnectionConfig.custom()
                    .setConnectTimeout(Timeout.of(Duration.ofMillis(30000))) // 设置连接超时
                    .setSocketTimeout(Timeout.of(Duration.ofMillis(60000))) // 设置 socket 超时
                    .setTimeToLive(Timeout.of(Duration.ofMillis(30000))) // 设置生存时间
                    .setValidateAfterInactivity(Timeout.of(Duration.ofMillis(10000))) // 设置在不活动之后验证
                    .build();
            connectionManager.setDefaultConnectionConfig(connectionConfig);

            //连接回收
//            connectionManager.closeExpired();
//            connectionManager.closeIdle(TimeValue.of(Duration.ofMillis(60000)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建httpclient对象
     * @param config 请求配置参数
     * @return
     * @throws Exception
     */
    public static CloseableHttpClient getHttpClient(RequestConfig config) throws Exception {
        return getHttpClient(config, null);
    }

    /**
     * 创建httpclient对象
     * @param config 请求配置参数
     * @param routePlanner 路由配置，可设置本地网络配置
     * @return
     * @throws Exception
     */
    public static CloseableHttpClient getHttpClient(RequestConfig config, HttpRoutePlanner routePlanner) throws Exception {
        HttpClientBuilder httpClientBuilder = HttpClients.custom()
                .setConnectionManager(connectionManager)
                .setConnectionManagerShared(true)
                .setDefaultRequestConfig(config)
                .setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, TimeValue.of(3, TimeUnit.SECONDS)));

        if (routePlanner != null) {
            httpClientBuilder.setRoutePlanner(routePlanner);
        }

        CloseableHttpClient httpClient = httpClientBuilder.build();

//        httpClient.close(CloseMode.GRACEFUL);
        return httpClient;
    }
}
